package com.zhu.salesmanagement.network;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.SocketException;
import java.net.SocketTimeoutException;

import org.apache.http.conn.EofSensorInputStream;
import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.params.ConnRouteParams;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;

import com.zhu.location.R;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Proxy;
import android.net.Uri;
import android.os.Environment;
import android.widget.RemoteViews;

public class DownloadService implements DownloadProgressListener{

    private final static int NOTIFICATION_ID = 1001;
    private final static int BUFFER_SIZE = 1 * 1024;
    private final static int UPDATE_PER_COUNT = 40;
    private final String UPDATE_SERVERAPK = "DMS_Android.apk";

    private Context mContext = null;
    private NotificationManager mNotifManager;
    private Notification mNotification = null;

    public DownloadService(Context context){
        mContext = context;
        mNotifManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
    }

    public void startDownLoad(String url, long size){
        new Task(url, size, this).startTask();
    }

    public class Task implements Runnable {
        private Thread mThread;
        private OutputStream mOutputStream = null;
        private InputStream mInputStream;
        private String mUrl;
        private long mTotalSize;
        private boolean interrupted = false;
        private DownloadProgressListener mListener = null;

        public Task(String url, long size, DownloadProgressListener listener){
            mUrl = url;
            mListener = listener;
            mTotalSize = size;
        }

        public void startTask() {
            mThread = new Thread(this);
            mThread.start();
        }

        @Override
        public void run() {
            // TODO Auto-generated method stub
            boolean success = false;
            try {
                long totalNum = 0;

                mListener.onDownloadStart(mContext, mUrl, 0, mTotalSize);

                mInputStream = getAppContentStream(mUrl);

                final InputStream input = mInputStream;

                mOutputStream = getApkCacheStream();
                if (input == null || mOutputStream == null) {
                    throw new IOException();
                }

                byte[] b = new byte[BUFFER_SIZE];
                int readNum = 0;
                int updateCount = UPDATE_PER_COUNT;
                if(isWifiConnected(mContext)) {
                    updateCount = UPDATE_PER_COUNT * 3;
                }

                int times = 0;
                while (!interrupted /*&& totalNum < mTotalSize*/
                        && (readNum = readFromStream(input, b, 0,
                                BUFFER_SIZE)) > 0) {
                    if (interrupted) {
                        break;
                    }
                    mOutputStream.write(b, 0, readNum);
                    totalNum += readNum;

                    if (times++ % updateCount == 0) {
                        mListener.onUpdateProgress(mContext, mUrl,
                                    totalNum, mTotalSize);
                    }
                }
                mOutputStream.flush();

                if (!interrupted && totalNum == mTotalSize) {
                    success = true;
                }
            } catch (Throwable e) {
                e.printStackTrace();
                if(!success && !interrupted) {
                    interrupted = true;
                }
            } finally {
                try {
                    if (mInputStream != null) {
                        if (mInputStream instanceof EofSensorInputStream) {
                            ((EofSensorInputStream) mInputStream)
                                    .abortConnection();
                        } else {
                            mInputStream.close();
                        }
                    }

                    if (mOutputStream != null) {
                        mOutputStream.close();
                    }
                } catch (Throwable f) {
                }

                mListener.onDownloadComplete(mContext, mUrl, null, success, interrupted);
            }
        }

        public int readFromStream(InputStream inputStream, byte[] b,
                int offset, int length) throws IOException {

            if (offset > b.length || offset < 0) {
                throw new ArrayIndexOutOfBoundsException(
                        "Offset out of bounds: " + offset);
            }
            if (length < 0 || length > b.length - offset) {
                throw new ArrayIndexOutOfBoundsException(
                        "Length out of bounds: " + length);
            }
            for (int i = 0; i < length; i++) {
                int c;
                try {
                    if ((c = inputStream.read()) == -1) {
                        return i == 0 ? -1 : i;
                    }
                } catch (IOException e) {
                    if (i != 0) {
                        return i;
                    }
                    throw e;
                }
                b[offset + i] = (byte) c;

                if (interrupted) {
                    return i + 1;
                }
            }
            return length;
        }
    }

    @Override
    public void onDownloadStart(Context context, String _id, long bytesSoFar, long totalBytes) {
        // TODO Auto-generated method stub
        startNotif();
    }

    @Override
    public void onDownloadComplete(Context context, String _id, String path, boolean success,
            boolean interrupted) {
        // TODO Auto-generated method stub
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setDataAndType(Uri.fromFile(new File(Environment.getExternalStorageDirectory(), UPDATE_SERVERAPK))
                , "application/vnd.android.package-archive");
        mContext.startActivity(intent);

        mNotifManager.cancel(NOTIFICATION_ID);
    }

    @Override
    public void onUpdateProgress(Context context, String _id, long bytesSoFar, long totalBytes) {
        int progress = (int)(bytesSoFar * 100 / totalBytes);
        mNotification.contentView.setTextViewText(R.id.percent, progress + "%");
        mNotification.contentView.setProgressBar(R.id.progress_bar, 100, progress, false);
        mNotifManager.notify(NOTIFICATION_ID, mNotification);
    }

    public synchronized void startNotif(){
        Notification notif = new Notification(R.drawable.ic_launcher, mContext.getString(R.string.notificationtitle), System.currentTimeMillis());

        RemoteViews downloadView = new RemoteViews(mContext.getPackageName(), R.layout.statusbar_download);
        downloadView.setImageViewResource(R.id.icon, android.R.drawable.stat_sys_download);
        downloadView.setTextViewText(R.id.title, mContext.getString(R.string.notificationtitle));
        downloadView.setTextViewText(R.id.percent, 0+"%");
        downloadView.setProgressBar(R.id.progress_bar, 100, 0, false);

        notif.icon = android.R.drawable.stat_sys_download;
        notif.when = 0;
        notif.flags = Notification.FLAG_ONGOING_EVENT;
        notif.defaults = 0;
        notif.sound = null;
        notif.vibrate = null;
        notif.tickerText = mContext.getString(R.string.notificationstarttitle);
        notif.contentView = downloadView;

        Intent intent = new Intent();
        PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0, intent,
                        PendingIntent.FLAG_UPDATE_CURRENT);
        notif.contentIntent = contentIntent;

        mNotification = notif;

        mNotifManager.notify(NOTIFICATION_ID, notif);
    }

    public InputStream getAppContentStream(final String url) throws SocketException {
        if (url == null) {
            return null;
        }
        final String getURL = url;
        HttpClient httpclient = getHttpClient();
        HttpGet httpget = createHttpRequest(mContext, getURL, true);
        try {
            httpget.addHeader("Range", "bytes=" + 0 + "-");
            HttpResponse response = httpclient.execute(httpget);
            if (response != null) {
                return response.getEntity().getContent();
            }
        } catch (Throwable e) {
            if (e instanceof SocketException
                    || e instanceof SocketTimeoutException) {
                throw new SocketException(e.getMessage());
            }
        } finally {

        }
        return null;
    }

    private synchronized OutputStream getApkCacheStream(){
        File file = new File(Environment.getExternalStorageDirectory(), UPDATE_SERVERAPK);
        if(file.exists()){
            file.delete();
        }
        FileOutputStream fileOutputStream = null;
        try {
            file.createNewFile();
            fileOutputStream = new FileOutputStream(file);
        } catch (Exception e) {
            e.printStackTrace();
            if(fileOutputStream != null){
                fileOutputStream.close();
                fileOutputStream = null;
            }
        }finally{
            return fileOutputStream;
        }
    }

    public static HttpClient getHttpClient() {
        HttpParams httpParams = new BasicHttpParams();
        HttpConnectionParams.setConnectionTimeout(httpParams, 20 * 1000);
        HttpConnectionParams.setSoTimeout(httpParams, 20 * 1000);
        HttpConnectionParams.setSocketBufferSize(httpParams, 8192);
        HttpClient httpClient = new DefaultHttpClient(httpParams);
        return httpClient;
    }

    @SuppressWarnings("unchecked")
    public static <T> T createHttpRequest(Context context, String url, boolean isGetRequest) {
        String proxyHost = null;
        int proxyPort = -1;
        boolean useProxy = false;

        ConnectivityManager connectivity = (ConnectivityManager) context
                .getSystemService(Context.CONNECTIVITY_SERVICE);
        
        NetworkInfo networkInfo = connectivity == null ? null : connectivity.getActiveNetworkInfo();

        if (networkInfo != null && networkInfo.getType() != ConnectivityManager.TYPE_WIFI) {
            proxyHost = Proxy.getHost(context);
            proxyPort = Proxy.getPort(context);
            useProxy = (proxyHost != null && proxyPort > 0);
        }

        HttpRequest request = null;

        if (isGetRequest) {
            try{
                request = new HttpGet(new String(url.getBytes(),"UTF-8"));
            }catch(UnsupportedEncodingException e){}
        } else {
            try{
                request = new HttpPost(new String(url.getBytes(),"UTF-8"));
            }catch(UnsupportedEncodingException e){}
        }
        if(request == null) {
            return null;
        }
        if (useProxy) {
            HttpHost proxy = new HttpHost(proxyHost, proxyPort);
            ConnRouteParams.setDefaultProxy(request.getParams(), proxy);
        }

        return (T) request;
    }

    public static boolean isWifiConnected(Context context) {
        ConnectivityManager connMgr = (ConnectivityManager) context
                .getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
        return networkInfo.isConnected();
    }
}
